import 'dart:async';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter_popover/src/color_tap.dart';
import 'package:flutter_popover/src/pop_decorator.dart';
import 'package:flutter_popover/src/ui_helper.dart';

class PopOver extends StatefulWidget {
  final Widget child;
  final List<PopOverItem> items;
  final ValueChanged<PopOverItem> onItemClick;
  final PopDecorator decorator;

  final AlignmentGeometry alignment;

  const PopOver({
    Key key,
    @required this.child,
    this.items,
    this.onItemClick,
    this.alignment = Alignment.bottomRight,
    @required this.decorator,
  }) : super(key: key);

  @override
  _PopOverState createState() => _PopOverState();
}

class _PopOverState extends State<PopOver> {
  GlobalKey key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    if (widget.items == null ||
        widget.items.isEmpty ||
        widget.onItemClick == null) {
      return widget.child;
    }

    var decorator = widget.decorator ??
        PopDecorator(
          maxWidth: MediaQuery.of(context).size.width * 0.8,
        );
    return PopDecoratorTheme(
      data: decorator,
      child: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () async {
          var rect = UIHelper.findGlobalRect(key);
          var item = await _showPopDialog(
            context: context,
            offset: Offset.zero,
            clickRect: rect,
            alignment: widget.alignment,
            list: widget.items,
            decorator: decorator,
          );
          if (item != null) widget?.onItemClick?.call(item);
        },
        child: Container(
          key: key,
          child: widget.child,
        ),
      ),
    );
  }
}

class PopOverItem {
  final Widget icon;
  final String text;
  final String keyLabel;

  const PopOverItem(
    this.icon,
    this.text,
    this.keyLabel,
  );
}

class _PopItem extends StatelessWidget {
  final PopOverItem popItem;
  final bool isLast;
  final PopDecorator decorator;

  const _PopItem({
    Key key,
    this.popItem,
    this.isLast = false,
    this.decorator,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    List<Widget> items = [];
    if (popItem.icon != null) {
      items.add(
        Container(
          height: decorator.itemHeight,
          child: popItem.icon,
        ),
      );
    }

    items.add(
      Expanded(
        child: Text(
          popItem.text,
          style: TextStyle(
            fontSize: 14.0,
            color: Colors.white,
            fontWeight: FontWeight.bold,
            decoration: TextDecoration.none,
          ),
          maxLines: 1,
        ),
      ),
    );

    return Container(
      height: decorator.itemHeight,
      width: double.infinity,
      child: Row(
        children: items,
      ),
    );
  }
}

const duration = const Duration(milliseconds: 300);

Future<PopOverItem> _showPopDialog({
  BuildContext context,
  List<PopOverItem> list,
  Rect clickRect,
  Offset offset,
  PopDecorator decorator,
  AlignmentGeometry alignment,
}) {
  if (list == null || list.isEmpty) {
    return Future.sync(() => null);
  }

  Completer<PopOverItem> completer = Completer();

  double maxHeight = decorator.itemHeight;
  double height = math.min(maxHeight, maxHeight + 4);

  OverlayEntry entry;
  var opacity = 0.0;

  bool init = false;

  entry = OverlayEntry(builder: (c) {
    return StatefulBuilder(
      builder: (_, s) {
        if (!init) {
          init = true;
          Timer(Duration.zero, () {
            opacity = 1.0;
            s(() {});
          });
        }
        dismiss(PopOverItem item) {
          s(() {
            opacity = 0.0;
          });
          Timer(duration, () {
            completer.complete(item);
            entry.remove();
          });
        }

        var items = list.map((item) {
          return ColorTap(
            onTap: () => dismiss(item),
            child: _PopItem(
              popItem: item,
              isLast: item == list[list.length - 1],
            ),
          );
        }).toList();

        Widget divider = Container(
          height: 1,
          margin: const EdgeInsets.symmetric(horizontal: 8.0),
          color: decorator.dividerColor,
        );
        return AnimatedOpacity(
          opacity: opacity,
          duration: duration,
          child: buildPopList(
            context: context,
            dismiss: dismiss,
            clickRect: clickRect,
            alignment: alignment,
            size: Size(decorator.itemHeight, decorator.itemHeight),
            popHeight: height,
            items: items,
            divider: divider,
            overlayBackgroundColor:
                decorator.popBackgroundColor ?? Colors.black.withOpacity(0.5),
            opacity: opacity,
          ),
        );
      },
    );
  });
  Overlay.of(context).insert(entry);

  return completer.future;
}

Widget buildPopList({
  BuildContext context,
  ValueSetter<PopOverItem> dismiss,
  Rect clickRect,
  Size size,
  double popHeight,
  List<Widget> items,
  Widget divider,
  Color overlayBackgroundColor,
  AlignmentGeometry alignment,
  double opacity = 1.0,
}) {
  Widget widget;

  widget = Positioned(
    top: _calcTop(
      context,
      clickRect,
      popHeight,
      alignment: alignment,
    ),
    left: _calcLeft(
      context,
      clickRect,
      size.width,
      alignment: alignment,
    ),
    height: popHeight,
    width: size.width,
    child: MediaQuery.removePadding(
      child: ClipRRect(
        borderRadius: BorderRadius.circular(4.0),
        child: Container(
          color: Colors.white,
          child: ListView.separated(
            itemBuilder: (_, i) => items[i],
            separatorBuilder: (_, __) => divider,
            itemCount: items.length,
          ),
        ),
      ),
      context: context,
      removeTop: true,
      removeBottom: true,
      removeRight: true,
      removeLeft: true,
    ),
  );
  widget = Stack(
    children: <Widget>[
      widget,
    ],
  );

  widget = AnimatedContainer(
    width: double.infinity,
    height: double.infinity,
    color: (overlayBackgroundColor ?? Colors.black.withOpacity(0.5))
        .withOpacity(opacity * 0.5),
    child: widget,
    duration: duration,
  );

  widget = GestureDetector(
    onTap: () => dismiss(null),
    child: widget,
  );
  return widget;
}

double _calcTop(
  BuildContext context,
  Rect clickRect,
  double height, {
  Alignment alignment = Alignment.bottomRight,
}) {
  var rect = alignment.withinRect(clickRect);
  var top = rect.dy < 0 ? 0 : rect.dy;

  var mqHeight = MediaQuery.of(context).size.height;

  if (top + height > mqHeight) {
    return mqHeight - height;
  }

  return top;
}

double _calcLeft(
  BuildContext context,
  Rect clickRect,
  double width, {
  Alignment alignment = Alignment.bottomLeft,
}) {
  var rect = alignment.withinRect(clickRect);
  var left = rect.dx < 0 ? 0 : rect.dx;

  var mqWidth = MediaQuery.of(context).size.width;

  if (left + width > mqWidth) {
    return mqWidth - width;
  }

  return left;
}
